#include "Client.h"

Client::Command* commands[] = {
	new Client::Command("quit", "Exit the program", "/q or /quit"),
	new Client::Command("help", "Displays this help file.", "/h or /help"),
	new Client::Command("connect", "Connects to a server.", "/c or /connect [ip or domain]"),
	new Client::Command("disconnect", "Disconnects from current server.", "/d or /disconnect"),
	new Client::Command("info", "Shows information about your client and it's connection.", "/i or /info"),
	new Client::Command("list", "Lists the users on server.", "/l or /list"),
	new Client::Command("send", "Send file to person.", "/s or /send <nickname> /path/to/file"),
	new Client::Command("accept", "Accept a file from another user.", "/a or /accept [filename]"),
	new Client::Command("reject", "Reject a file from another user.", "/r or /reject [filename]")
};

bool marked_for_deletion (const Participant* p) { return p->mark_for_deletion; }
bool ft_marked_for_deletion (const FileTransfer* f) { return (f->status == COMPLETE || f->status == CANCELED); }

Client::Client(void)
{
	//	filePort = FILE_PORT;
	cursor = 0;
	scrollPos = 0;
	characters = 0;
	message[0] = '\0';
	host = NULL;
	isRunning = true;

	// Empty switcher set
	FD_ZERO(&switcher);
	// Watch STDIN for input.
	FD_SET(fileno(stdin), &switcher);

	max_fd = fileno(stdin);
}

Client::~Client( void )
{
	disconnect();
}

#ifdef __WIN32__
void Client::run(void)
{
	DWORD dwWait = 0;
	Dispatch *packet = NULL;
	unsigned int num_handles = 1;
	int handle = 0;

	HANDLE handles[] = {
		GetStdHandle(STD_INPUT_HANDLE),
		WSA_INVALID_EVENT
	};

	while (isRunning)
	{
		// Check to see if we have a host.
		// If we do, create an event, and
		// configure it with listening to
		// the socket for a 'ready to read'
		if (host != NULL)
		{
			num_handles = 2;
			handles[1] = WSACreateEvent();
			WSAEventSelect((SOCKET)chat_socket, handles[1], FD_READ);

		} else num_handles = 1;

		// Wait on the handles, (STDIN if no connection, STDIN and socket if there is)
		dwWait = WaitForMultipleObjects(num_handles,	//number of event objects
										handles,	// array of event objects
										FALSE,			// does not wait for all
										INFINITY);		// Waits forever

		// Which handle is ready?
		handle = dwWait - WAIT_OBJECT_0;

		if (handle < 0 || handle > (num_handles - 1)) //NUM_HANDLES = 2
		{
			addLine(0, "Index out of range.");
			printf("Index out of range.\n");

			// Event was the socket having something to read.
		} else if (handle == 1)
		{
			// New dispatch
			packet = new Dispatch();

			// Read from the socket
			if ( packet->receive(chat_socket) == -1 )
			{
				addLine(0, "Error recving from socket stream.");

			} else if(packet->getFlag() == SERVER_DISCONNECT)
			{
				addLine(0, "Server disconnected");
				disconnect();

			} else processDispatch(packet);

			delete packet;

			// Otherwise, it was an input event.
		} else catchInput();

		updateWindow();
	}
}
#else
void Client::run(void)
{
	fd_set readfrom;
	fd_set writeto;
	Dispatch *packet = NULL;
	int local_max_fd;

	while (isRunning)
	{
		local_max_fd = max_fd;
		readfrom = switcher;
		FD_ZERO(&writeto);

		if(host != NULL)
		{
			// Are there any pending
			if ( anyActiveFileTransfers(i_transfers) )
			{
				FD_SET(file_socket, &readfrom);
				local_max_fd = file_socket > local_max_fd ? file_socket : local_max_fd;
			}

			if ( anyActiveFileTransfers(o_transfers) )
			{
				FD_SET(file_socket, &writeto);
				local_max_fd = file_socket > local_max_fd ? file_socket : local_max_fd;
			}

			select(local_max_fd+1, &readfrom, &writeto, NULL, 0);

			// File data to transfer (write)!
			if ( FD_ISSET(file_socket, &writeto) )
				handleOutgoingFileTransfers();

			// File data to transfer (read)!
			if ( FD_ISSET(file_socket, &readfrom) )
				handleIncomingFileTransfers();

		} else select(max_fd+1, &readfrom, NULL, NULL, 0);

		if(FD_ISSET(fileno(stdin), &readfrom))
		{
			catchInput();
		} else if(host != NULL && FD_ISSET(chat_socket, &readfrom))
		{
			// New dispatch
			packet = new Dispatch();

			if(packet->receive(chat_socket) == -1)
			{
				addLine(0, "Error recving from socket stream.");
			} else if(packet->getFlag() == SERVER_DISCONNECT)
			{
				addLine(0, "Server disconnected");
				disconnect();
			} else {
				processDispatch(packet);
			}

			delete packet;
		}
		updateWindow();
	}

}
#endif

void Client::establishUDPConnection( const std::string ip )
{
/* create a socket
 * IP protocol family (PF_INET)
 * UDP protocol (SOCK_DGRAM)
 *****************************************/

	if ( (file_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET) return perror("socket");

#ifdef __WIN32__
	u_long mode = 1;
	ioctlsocket(file_socket, FIONBIO, &mode);
#else
	fcntl(file_socket, F_SETFL, O_NONBLOCK);
#endif

	file_connection.sin_family = AF_INET;					// host byte order
	file_connection.sin_port = htons(FILE_PORT);			// short, network byte order
	file_connection.sin_addr = *((struct in_addr *)host->h_addr);
	memset(&(file_connection.sin_zero), '\0', 8);			// zero the rest of the struct

	char data[sizeof(uint16_t)];
	encode_data(data, uid);

	// Let server know about out existance. (It will record our public IP and Port)
	if (sendto(file_socket, data, sizeof(uint16_t), 0, (struct sockaddr*)(&file_connection), sizeof(struct sockaddr))==-1)
		return perror("establishUDPConnection: sendto");
}

bool Client::establishTCPConnection( const std::string ip )
{
	// Disconnect before connecting.
	// TODO: Confirm with user?
	disconnect();

	new_connection = true;

	addLine(0, "Opening a TCP connection...");

	if ( !(host = gethostbyname(ip.c_str())) )
	{
		addLine(0, "Invalid host or IP address, try again.");
		disconnect();
		return false;
	} else {

		/* create a socket
		 IP protocol family (PF_INET)
		 TCP protocol (SOCK_STREAM)
		 */
		if((chat_socket = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
		{
			addLine(0, strerror(errno));
			disconnect();
			return false;
		}

		chat_connection.sin_family = AF_INET;	// host byte order
		chat_connection.sin_port = htons(TEXT_PORT);  // short, network byte order
		chat_connection.sin_addr = *((struct in_addr *)host->h_addr);
		memset(&(chat_connection.sin_zero), '\0', 8);  // zero the rest of the struct

		if(connect(chat_socket, (struct sockaddr *)&chat_connection, sizeof(struct sockaddr)) == -1)
		{
			addLine(0, strerror(errno));
			disconnect();
			return false;
		}

		// Add socket to master set
		FD_SET(chat_socket, &switcher);

		// Set the maximum file descriptor
		max_fd = max_fd > chat_socket ? max_fd : chat_socket;

		addLine(0, "TCP Connection established.");
		addLine(0, "Requesting participation...");
		Dispatch packet = Dispatch(CLIENT_REQUEST_CONNECTION, username);
		packet.release(chat_socket);
	}
	return true;
}

bool Client::anyActiveFileTransfers(std::list<FileTransfer*>& transferList)
{
	std::list<FileTransfer*>::iterator l_it;
	for ( l_it=transferList.begin() ; l_it != transferList.end(); l_it++ )
		if ((*l_it)->status == ACCEPTED) return true;
	return false;
}

void Client::handleIncomingFileTransfers( void )
{
	std::list<FileTransfer*>::iterator fl_it;
	FileTransfer *f;
	FileTransferData *ftd = new FileTransferData;
	Participant *p;

	struct sockaddr addr;
	socklen_t fromlen = sizeof(addr);

	size_t length = sizeof(FileTransferData) + DATA_SIZE;
	char *buffer = (char*)malloc(length);
	char *read_ptr = buffer;

	// NOTE: Not length, because we don't want to accidentally overread.
	recvfrom(file_socket, buffer, sizeof(FileTransferData), 0, &addr, &fromlen);
	memcpy(ftd, read_ptr, sizeof(FileTransferData));
	read_ptr += sizeof(FileTransferData);

	recvfrom(file_socket, read_ptr, ftd->data_length, 0, &addr, &fromlen);

	for ( fl_it=i_transfers.begin() ; fl_it != i_transfers.end(); fl_it++ )
	{
		f = (*fl_it);
		if (f->uid == ftd->ft_uid)
		{
			p = f->client;

			if (feof(f->file))
			{
				f->status = COMPLETE;
				addLine(0, "Transfer of file '"+f->filename+"' from '"+f->client->username+"' complete.");
			} else if (f->status == ACCEPTED)
			{
			//	if (ftd->data_offset > f->bytes_transfered)
			//		addLine(0, "No good!");

				// position head
				fseek ( f->file, ftd->data_offset, SEEK_SET );
				fwrite(read_ptr, sizeof(char), ftd->data_length, f->file);

				f->bytes_transfered += ftd->data_length;

				if (ferror(f->file))
				{
					addLine(0, "Error writing file.");
					f->status = CANCELED;
					fclose(f->file);
				}

			}

			// Found it. Moving on.
			break;
		}
	}

	free(buffer);
	delete ftd;

	i_transfers.remove_if(ft_marked_for_deletion);
}

void Client::handleOutgoingFileTransfers( void )
{
	std::list<FileTransfer*>::iterator fl_it;
	FileTransfer *f;
	FileTransferData *ftd;
	Participant *p;

	char *buffer = NULL;
	char *write_ptr;
	uint16_t total;
	int sent;

	for ( fl_it=o_transfers.begin() ; fl_it != o_transfers.end(); fl_it++ )
	{
		f = (*fl_it);
		p = f->client;

		if (feof(f->file))
		{
			f->status = COMPLETE;
			addLine(0, "Transfer of file '"+f->filename+"' to '"+f->client->username+"' complete.");
		} else if (f->status == ACCEPTED)
		{
			ftd = new FileTransferData;

			ftd->client_uid = uid;
			ftd->data_offset = ftell(f->file);
			ftd->ft_uid = f->uid;

			size_t length = sizeof(FileTransferData) + DATA_SIZE;
			buffer = (char*)malloc(length);
			write_ptr = buffer + sizeof(FileTransferData);

			ftd->data_length = fread(write_ptr, sizeof(char), DATA_SIZE, f->file);
			write_ptr += ftd->data_length;

			memcpy(buffer, ftd, sizeof(FileTransferData));

			if (ferror(f->file))
			{
				addLine(0, "Error reading file.");
				f->status = CANCELED;
				fclose(f->file);
			}

			total = sizeof(FileTransferData)+ftd->data_length;

			while ( total > 0 )
			{
				sent = sendto(file_socket, buffer, total, 0, (struct sockaddr*)(&p->file_connection), sizeof(struct sockaddr));
				if (sent == -1)
				{
					perror("establishUDPConnection: sendto");
					break;
				}
				total -= sent;
			}

			free(buffer);

			delete ftd;
		}
	}

	o_transfers.remove_if(ft_marked_for_deletion);
}

void Client::handleOutgoingClientMessage( void )
{
	size_t length = sizeof(uint16_t)+characters;
	char *buffer = (char*)malloc(length+1);
	char *write_ptr = buffer;

	write_ptr += encode_data(write_ptr, uid);
	memcpy(write_ptr, message, characters);
	write_ptr[characters] = '\0';

	Dispatch packet = Dispatch(CLIENT_MESSAGE);
	packet.setData(buffer, length+1);
	if(!packet.release(chat_socket))
		addLine(0, "Connection Error.");
	else
		addLine(1, username + " >> " + message);

	free(buffer);
}

void Client::handleIncomingClientMessage( Dispatch *d )
{
	uint16_t clientid;
	Participant* p;

	const char *buffer = d->getRawData();
	const char *read_ptr = buffer;

	read_ptr += decode_data(read_ptr, &clientid);

	if ( (p = findParticipantByUID(clientid)) != NULL )
		addLine(2, p->username + ": "+read_ptr);
}

void Client::updateUserList( std::string data, bool silent )
{
	char clientid = '\0';
	char username[32];
	std::list<Participant*>::iterator p_it;
	Participant *p;
	std::stringstream ss(data);
	std::string buffer;

	// Mark all participants for deletion. If they show up in the userlist they will not be deleted.
	for ( p_it = participants.begin(); p_it != participants.end(); p_it++ ) (*p_it)->mark_for_deletion = true;

	while (ss >> buffer) if ( sscanf(buffer.c_str(), "%c:%s", &clientid, username) == 2 )
	{
		// Search for existing participant
		if ( (p = findParticipantByUID(clientid)) != NULL)
		{
			// Participant is present in userlist, no longer marked for deletion
			p->mark_for_deletion = false;

			if(p->username.compare(username) != 0)
			{
				if (!silent) addLine(0, p->username + " is now known as " + username);
				p->username = username;
			}
		} else
		{
			p = new Participant();
			p->username = std::string(username);
			p->uid = (unsigned int)clientid;
			p->mark_for_deletion = false;
			p->file_connection.sin_family = AF_INET;
			participants.push_back(p);
			if (!silent) addLine(0, p->username + " joined the room.");
		}

	} else
	{
		addLine(0, "Malformed userlist!");
		return;
	}

	// TODO: Loop, find all marked for deletion, addLine("so and so left the room");
	// Remove all participants if marked for deletion
	participants.remove_if(marked_for_deletion);
}

void Client::listUsers( void )
{
	std::string list;
	std::list<Participant*>::iterator p_it;
	for ( p_it = participants.begin(); p_it != participants.end(); p_it++ )
	{
		if (p_it != participants.begin()) list += ", ";
		list += (*p_it)->username;
	}

	char buffer[256];
	sprintf(buffer, "Users currently in the room: (%lu) %s", participants.size(), list.c_str());

	addLine(0, buffer);
}

void Client::disconnect()
{
	if (host == NULL) return;
	Dispatch d = Dispatch(CLIENT_DISCONNECT, username);
	d.release(chat_socket);

#if __WIN32__
	closesocket(chat_socket);
#else
	close(chat_socket);
#endif
	host = NULL;
	addLine(0, "Connection Closed.");

	// Remove socket from master set.
	FD_CLR(chat_socket, &switcher);
}

// deprecated
// yes but it was still cool.
bool Client::validIP( const std::string ip)
{
	int d;
	return sscanf(ip.c_str(), "%d.%d.%d.%d", &d, &d, &d, &d) == 4;
}

void Client::updateInfo( std::string data )
{
	char u[32];
	char clientid = '\0';
	if (sscanf(data.c_str(), "%c:%s", &clientid, u) == 2)
	{
		uid = (unsigned int)clientid;
		setUsername(u);
	} else addLine(-1, "Malformed Client Info Packet.");
}

void Client::processDispatch (Dispatch *packet)
{
	switch (packet->getFlag())
	{
		case CLIENT_MESSAGE:
			handleIncomingClientMessage(packet);
			break;

		case SERVER_DICTATE_INFO:
			updateInfo(packet->getData());
			if (new_connection)
			{
				addLine(0, "Client participation accepted. Welcome " + username);
				establishUDPConnection( inet_ntoa(chat_connection.sin_addr) );
			}
			break;

		case FILE_SEND_ACCEPT:
			handleOutgoingFileAccept(packet);
			break;

		case FILE_SEND_REQUEST:
			handleIncomingFileRequest(packet);
			break;

		case SERVER_USER_LIST:
//			updateUserList(packet->getData(), false);
			updateUserList(packet->getData(), new_connection);
			new_connection = false;
			break;

			//case CONFIRM:
			//case REQUEST:fileRequest = true;

		default:
			// I think we should have a corrupt data counter. So if we recieve to many, we disconnect (viruses and shit).
			addLine(-1, std::string("Recieved Corrupt Data.") + packet->getData());
			break;
	}
}

void Client::processCommands(char* message)
{
	char command[16];
	char args[64];
	args[0] = '\0';

	sscanf(message, "/%s %[^\t\n]", command, args);
	if		(strcmp(command,"quit") == 0
			 || strcmp(command,"q") == 0)		quit(args);
	else if (strcmp(command,"send") == 0
			 || strcmp(command,"s") == 0)		handleOutgoingFileRequest(args);
	else if (strcmp(command,"help") == 0
			 || strcmp(command,"h") == 0)		help(args);
	else if (strcmp(command,"connect") == 0
			 || strcmp(command,"c") == 0)		establishTCPConnection(args);
	else if (strcmp(command,"disconnect") == 0
			 || strcmp(command,"d") == 0)		disconnect();
	else if (strcmp(command,"nick") == 0
			 || strcmp(command,"n") == 0)		nick(args);
	else if (strcmp(command,"info") == 0
			 || strcmp(command,"i") == 0)		info(args);
	else if (strcmp(command,"list") == 0
			 || strcmp(command,"l") == 0)		listUsers();
	else if (strcmp(command,"accept") == 0
			 || strcmp(command,"a") == 0)		handleIncomingFileAccept(args);
	//	else if (strcmp(command,"setport") == 0)	set_port (args);
	//	else if (strcmp(command,"sendfile") == 0)	sendFile(args);
	//	else if (strcmp(command,"prefs") == 0)		do_prefs ();
	//	else if (strcmp(command,"settings") == 0)	list_settings();
	else	addLine(0, "Invalid Command.");
}

void Client::info(const char *args)
{
	if (host != NULL)
		addLine(0, std::string(username) + ", you are connected to " + std::string(inet_ntoa(*(in_addr *)host->h_addr)));
	else
		addLine(0, std::string(username) + ", you are not currently connected to any server.");
}

void Client::help(const char *args)
{
	int cmds = (sizeof(commands))/sizeof(Client::Command*);

	if (!strcmp(args, ""))
	{
		if (1 == 0)
			addLine(0, "No help file found, help has been disabled.");

		addLine(0, "\t============================================");
		addLine(0, "\tHelp! Below are the available commands.");
		addLine(0, "\tType `/help [command]' for more information.");
		addLine(0, "\t============================================");

		// For converting int to string.
		std::stringstream out;
		for (unsigned int i = 0; i < cmds; i++)
		{
			// Empty the converter
			out.str("");
			out << i+1;
			addLine(0, std::string("\t" + out.str() + ".\t`" + commands[i]->cmd) + "'\t\t" + commands[i]->dsc);
		}

		addLine(0, "\t============================================");
		addLine(0, "\tEnd help. Let's hope you are no longer lost!");
		addLine(0, "\t============================================");

	} else
	{
		for (int j = 0; j < cmds; j++)
		{
			if (std::string(args) == commands[j]->cmd)
			{
				addLine(0, std::string("Command: ") + commands[j]->cmd);
				addLine(0, commands[j]->dsc);
				addLine(0, std::string("Use: ") + commands[j]->use);
				return;
			}
		}
		addLine(0, std::string("No help avalible for ") + args);
	}
}

Participant *Client::findParticipantByUID( unsigned int p_id )
{
	std::list<Participant*>::iterator p_it;

	for ( p_it = participants.begin(); p_it != participants.end(); p_it++ )
		if ( (*p_it)->uid == p_id) return (*p_it);

	return NULL;
}

Participant *Client::findParticipantByUsername( std::string p_name )
{
	std::list<Participant*>::iterator p_it;

	for ( p_it = participants.begin(); p_it != participants.end(); p_it++ )
		if ( (*p_it)->username.compare(p_name) == 0) return (*p_it);

	return NULL;
}

void Client::handleOutgoingFileAccept( Dispatch *d )
{
	char *filename;
	FileTransferHandshake *ft_handshake = new FileTransferHandshake;
	std::list<FileTransfer*>::iterator fl_it;
	FileTransfer *f;
	Participant *p;

	// Format: CUID.FTUID.FN_LENGTH.FILENAME.PORT.IP
	const char *data = d->getRawData();
	const char *read_ptr = data;

	memcpy(ft_handshake, read_ptr, sizeof(FileTransferHandshake));
	read_ptr += sizeof(FileTransferHandshake);

	// Allocate space for the filename
	filename = (char*)malloc(ft_handshake->filename_size+1);
	// Extract filename from buffer.
	memcpy(filename, read_ptr, ft_handshake->filename_size);
	filename[ft_handshake->filename_size] ='\0';
	// advance read_ptr
	read_ptr += ft_handshake->filename_size;

	for ( fl_it=o_transfers.begin() ; fl_it != o_transfers.end(); fl_it++ )
	{
		f = (*fl_it);
		p = f->client;

		// Security checking
		// UID isn't working right now, using filename.
		if (f->filename.compare(filename) == 0 && f->client->uid == ft_handshake->client_uid)
		{
			f->client->file_connection.sin_port = ft_handshake->port;
			f->client->file_connection.sin_addr = ft_handshake->addr;

			f->status = ACCEPTED;
			addLine(0, f->client->username+" has chosen to accept your file '"+f->filename+"'. Transfer will begin immediately.");
			break;
		}
	}

	free(filename);
}

void Client::handleIncomingFileRequest( Dispatch *d )
{
	Participant *p;
	const char *data = d->getRawData();
	const char *read_ptr = data;
	FileTransferHandshake *ft_handshake = new FileTransferHandshake;
	char *filename;

	memcpy(ft_handshake, read_ptr, sizeof(FileTransferHandshake));
	read_ptr += sizeof(FileTransferHandshake);

	// Dispatch file send request. UID.FTUID.FN_LENGTH.FILENAME

	// Find participant by UID
	if ( (p = findParticipantByUID(ft_handshake->client_uid)) != NULL )
	{
		FileTransfer *f = new FileTransfer;
		f->client = p;
		f->uid = ft_handshake->ft_uid;
		f->bytes_transfered = 0;
		f->filesize = ft_handshake->filesize;
		f->status =	PENDING;

		// Allocate space for the filename
		filename = (char*)malloc(ft_handshake->filename_size+1);
		// Extract filename from buffer.
		memcpy(filename, read_ptr, ft_handshake->filename_size);
		filename[ft_handshake->filename_size] ='\0';
		// Assign filename to struct.
		f->filename = filename;
		// advance read_ptr
		read_ptr += ft_handshake->filename_size;

		f->client->file_connection.sin_port = ft_handshake->port;
		f->client->file_connection.sin_addr = ft_handshake->addr;

		i_transfers.push_back(f);
		addLine(0, p->username + " wants to send you the file '"+ f->filename +"'. /accept or /reject it.");
	} else
	{
		// Weird... we got a file send request from a user who does not exist...
		addLine(0, "Malformed incoming file request: "+ d->getData());
	}
}

void Client::handleIncomingFileAccept( std::string args )
{
	if (i_transfers.size() < 1) return addLine(0, "No pending incoming file transfers to accept.");

	// Accept the most recent (front) file transfer.
	if ( args.empty() )
	{
		FileTransfer *f = i_transfers.front();
		f->status = ACCEPTED;
		// open file for writing.
		f->file = fopen(std::string(f->client->username+"."+f->filename).c_str(), "w");

		addLine(0, "You've chosen to accept the file '"+f->filename+"' from "+f->client->username+". Transfer will begin immediately.");

		FileTransferHandshake *ft_handshake = new FileTransferHandshake;
		ft_handshake->client_uid = f->client->uid;
		ft_handshake->ft_uid = f->uid;
		ft_handshake->filename_size = f->filename.size();

		size_t length = sizeof(FileTransferHandshake)+f->filename.size();
		char *buffer = (char*)malloc(length);
		char *write_ptr = buffer;

		memcpy(write_ptr, ft_handshake, sizeof(FileTransferHandshake));
		write_ptr += sizeof(FileTransferHandshake);

		memcpy(write_ptr, f->filename.c_str(), f->filename.size());

		Dispatch *d = new Dispatch(FILE_SEND_ACCEPT, buffer, length);
		d->release(chat_socket);

		free(buffer);
		delete ft_handshake;
	} else
	{
		// TODO: using arguments, find file from queue that user is accepting
	}
}

void Client::handleIncomingFileReject( std::string args )
{
	if (i_transfers.size() < 1) return addLine(0, "No pending incoming file transfers to reject.");

	if ( args.empty() )
	{
		FileTransfer *f = i_transfers.front();

		size_t length = sizeof(uint16_t)+f->filename.size();
		char *buffer = (char*)malloc(length+1);
		char *write_ptr = buffer;

		//write_ptr += encode_data(write_ptr, f->client->uid);
		memcpy(write_ptr, f->filename.c_str(), f->filename.size());
		//write_ptr[f->filename.size()] = '\0';

		Dispatch *d = new Dispatch(FILE_SEND_REJECT, buffer, length);
		d->release(chat_socket);
		// Remove it from the queue
		i_transfers.pop_front();

		delete f;
	} else
	{
		// TODO: using arguments, find file from queue that user is rejecting
	}
}

void Client::handleOutgoingFileRequest( const char* args )
{
	char nick[16];
	char p[32];
	Participant* client;
	FILE *file;

	FileTransferHandshake *ft_handshake;

	// Split arguments
	sscanf(args, "%s %[^\t\n]", nick, p);

	// Check for correct usage of command.
	if (strlen(nick) == 0 || strlen(p) == 0) return help("send");

	std::string path(p);
	std::string filename;

#ifdef __WIN32__
	size_t pos = path.find_last_of("\\");
#else
	size_t pos = path.find_last_of("/");
#endif

	if(pos != std::string::npos)
		filename.assign(path.begin() + pos + 1, path.end());
	else
		filename = path;

	// Find ID of client with username -nick-
	if ( (client = findParticipantByUsername(nick)) != NULL )
	{
		// Open file for reading.
		if ( (file = fopen(filename.c_str(), "r")) != NULL )
		{
			addLine(0, std::string("Requesting to send file '") + path + "' to " + nick);

			FileTransfer *f = new FileTransfer;
			f->filename = filename;
			f->client = client;
			f->file = file;
			// Sender picks the FTUID
			f->uid = (uintptr_t)f;
			f->status =	PENDING;
			f->bytes_transfered = 0;

			fseek (file, 0, SEEK_END);
			f->filesize = ftell(file);
			rewind(file);

			o_transfers.push_back(f);

			ft_handshake = new FileTransferHandshake;
			ft_handshake->client_uid = client->uid;
			ft_handshake->ft_uid = f->uid;
			ft_handshake->filename_size = filename.size();
			ft_handshake->filesize = f->filesize;

			size_t length = sizeof(FileTransferHandshake) + filename.size();

			char *buffer = (char*)malloc(length+1);
			char *write_ptr = buffer;

			memcpy(write_ptr, ft_handshake, sizeof(FileTransferHandshake));
			write_ptr += sizeof(FileTransferHandshake);

			memcpy(write_ptr, filename.c_str(), filename.size());

			buffer[length] = '\0';

			Dispatch request = Dispatch(FILE_SEND_REQUEST, buffer, length);
			request.release(chat_socket);

			free(buffer);
			delete ft_handshake;

			return;
		} else
		{
			addLine(0, "Could not open the file '"+filename+"' for reading.");
		}
	} else
	{
		addLine(0, std::string(nick) + " is not present in the room.");
	}
}

void Client::_addLine(int s, const char *message, ...)
{
	char *buffer;
	va_list args;
	va_start(args, message);
	vasprintf(&buffer, message, args);
	va_end(args);
	addLine(s, std::string(buffer));
	free(buffer);
}

void Client::quit(const char* quitMessage)
{
	disconnect();
	isRunning = false;
}

void Client::setUsername( std::string u )
{
	if (username.compare(u) != 0)
		addLine(0, std::string("You are now known as ") + u);
	username = u;
}

void Client::nick(const char* nick)
{
	if (host != NULL)
	{
		Dispatch packet = Dispatch(CLIENT_REQUEST_NAME, nick);
		packet.release(chat_socket);
	} else {
		setUsername(nick);
	}
}

void Client::readLine (char* line, FILE* file)
{
	char* temp = line;
	int val = -1;
	while (val != '\r' && val != '\n')
	{
		val = getc(file);
		if (val == EOF)
		{
			if (feof(file))	break;
			else			temp = NULL;
		}
		*temp++ = val;
	}
	*temp = '\0';
}
